﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel.Composition;
using System.ComponentModel.Composition.Hosting;
using System.ComponentModel.Composition.Primitives;
using System.Linq;
using System.Text;
using Snowdreamist;
using Snowdreamist.Collection;
using Snowdreamist.Runtime;
using Snowdreamist.Runtime.Services;

namespace Snowdreamist.ComponentModel.MEF
{
    public class MEFModuleRepository : IModuleRepository
    {
        private static ExportProvider CreateDefaultExportProvider()
        {
            return new CompositionContainer(new AggregateCatalog(new ComposablePartCatalog[]
            {
                new DirectoryCatalog(Environment.CurrentDirectory)
            }));
        }

        public MEFModuleRepository() : this(null) { }

        public MEFModuleRepository(ExportProvider exportProvider)
        {
            if (exportProvider == null)
                exportProvider = CreateDefaultExportProvider();
            // import modules
            this._importedModules = exportProvider.GetExports<IModule, IModuleMetadata>();
            // init
            this._moduleDefinitions = new ConcurrentObservableCollection<IModuleDefinition>(this._importedModules.Select(p => new MEFModuleDefinition(p)));
            this.Modules = new ReadonlyConcurrentObservableCollection<IModuleDefinition>(this._moduleDefinitions);
        }

        public virtual void Initialize() { }

        public virtual void OnRuntimeDisposing()
        {
            this._moduleDefinitions.Dispose();
        }

        public ReadonlyConcurrentObservableCollection<IModuleDefinition> Modules { get; private set; }

        public IModuleDefinition GetModuleDefinition<T>()
        {
            var modules = this._moduleDefinitions.Where(p => p.ExportType == typeof(T)).ToArray();
            if (modules.Length == 0)
            {
                return null;
            }
            else if (modules.Length > 1)
            {
                throw new MultipleValuesFoundException();
            }
            else
            {
                return modules.First();
            }
        }

        public IModuleDefinition GetModuleDefinition(string name)
        {
            var modules = this._moduleDefinitions.Where(p => string.Compare(p.Name, name, true) == 0).ToArray();
            if (modules.Length == 0)
            {
                return null;
            }
            else if (modules.Length > 1)
            {
                throw new MultipleValuesFoundException();
            }
            else
            {
                return modules.First();
            }
        }

        public event EventHandler ModuleChanged;

        protected void OnModuleChanged()
        {
            if (this.ModuleChanged != null)
                this.ModuleChanged(this, new EventArgs());
        }

        private IEnumerable<Lazy<IModule, IModuleMetadata>> _importedModules;

        private ConcurrentObservableCollection<IModuleDefinition> _moduleDefinitions;
    }
}